package com.xu.cloud.common.process;

import com.xu.cloud.common.api.XuApi;
import com.xu.cloud.common.process.support.NopProcessorSelector;
import com.xu.cloud.common.process.support.XuApiAopProxyFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.*;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * 扫描并注册XuApi接口基于FactoryBean代理的BeanDefinition
 *
 * @author xuguan
 * @since 2025/1/17
 */
public class XuApiScannerRegistry extends ClassPathScanningCandidateComponentProvider implements EnvironmentAware, BeanDefinitionRegistryPostProcessor {

    public XuApiScannerRegistry() {
        addIncludeFilter(new AnnotationTypeFilter(XuApi.class));
    }

    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        boolean isCandidate = false;
        if (beanDefinition.getMetadata().isIndependent()) {
            if (!beanDefinition.getMetadata().isAnnotation()) {
                isCandidate = true;
            }
        }
        return isCandidate;
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        List<String> basePackages = new ArrayList<>();
        IProcessSelector processorSelector = new NopProcessorSelector();
        if (registry instanceof BeanFactory) {
            basePackages = AutoConfigurationPackages.get((BeanFactory) registry);
            try {
                processorSelector = ((BeanFactory) registry).getBean(IProcessSelector.class);
            } catch (BeansException e) {
                // nop
            }
        }

        for (String pkg : basePackages) {
            final Set<BeanDefinition> candidates = findCandidateComponents(pkg);
            for (BeanDefinition candidate : candidates) {
                final String beanClassName = candidate.getBeanClassName();
                final BeanDefinitionBuilder bdb = BeanDefinitionBuilder.genericBeanDefinition(XuApiAopProxyFactoryBean.class)
                        .addPropertyValue("target", beanClassName)
                        .addPropertyValue("processSelector", processorSelector)
                        .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
                final AbstractBeanDefinition bd = bdb.getBeanDefinition();
                bd.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, beanClassName);
                String beanName = beanClassName;
                if (candidate instanceof AnnotatedBeanDefinition) {
                    final Map<String, Object> attributes = ((AnnotatedBeanDefinition) candidate).getMetadata().getAnnotationAttributes(XuApi.class.getName());
                    if (Objects.nonNull(attributes) && StringUtils.hasText(String.valueOf(attributes.get("value")))) {
                        beanName = String.valueOf(attributes.get("value"));
                    }
                }
                final BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(bd, beanName);
                BeanDefinitionReaderUtils.registerBeanDefinition(beanDefinitionHolder, registry);
            }
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // nop
    }
}
